1 module hip.util.conv; 2 import hip.util.string; 3 import std.typecons; 4 import std.traits:isArray, isCallable; 5 public import hip.util.to_string_range; 6 public import hip.util.string:toStringz; 7 8 9 string toString(dstring dstr) pure nothrow @safe 10 { 11 try 12 { 13 string ret; 14 foreach(ch; dstr) 15 ret~= ch; 16 return ret; 17 } 18 catch(Exception e){return "";} 19 } 20 21 string toString(char[] arr) pure nothrow @trusted @nogc {return cast(string)arr;} 22 string toString(T)(T[] arr) pure nothrow @safe if(!is(T == char)) 23 { 24 string ret = "["; 25 for(int i = 0; i < arr.length; i++) 26 { 27 if(i) 28 ret~= ","; 29 ret~= toString(arr[i]); 30 } 31 return ret~"]"; 32 } 33 34 35 string toString(T)(T structOrTupleOrEnum) pure nothrow @safe if(!isArray!T) 36 { 37 static if(isTuple!T) 38 { 39 alias tupl = structOrTupleOrEnum; 40 string ret; 41 foreach(i, v; tupl) 42 { 43 if(i > 0) 44 ret~= ", "; 45 ret~= to!string(v); 46 } 47 return T.stringof~"("~ret~")"; 48 } 49 else static if(is(T == struct))//For structs declaration 50 { 51 import hip.util.reflection; 52 static if(__traits(hasMember, T, "toString") && hasUDA!(__traits(getMember, T, "toString"), "format")) 53 { 54 import hip.util.format; 55 return formatFromType(structOrTupleOrEnum); 56 } 57 else 58 { 59 alias struct_ = structOrTupleOrEnum; 60 string s = "("; 61 static foreach(i, alias m; T.tupleof) 62 { 63 if(i > 0) 64 s~= ", "; 65 s~= to!string(struct_.tupleof[i]); 66 } 67 return T.stringof~s~")"; 68 } 69 } 70 else static if(is(T == enum)) 71 { 72 foreach(mem; __traits(allMembers, T)) 73 if(__traits(getMember, T, mem) == structOrTupleOrEnum) 74 return T.stringof~"."~mem; 75 return T.stringof~".|MEMBER_NOT_FOUND|"; 76 } 77 else static assert(0, "Not implemented for "~T.stringof); 78 } 79 80 81 82 string dumpStructToString(T)(T struc) if(is(T == struct)) 83 { 84 string s = "\n("; 85 static foreach(i, alias m; T.tupleof) 86 { 87 s~= "\n\t "~m.stringof~": "; 88 s~= toString(struc.tupleof[i]); 89 } 90 return T.stringof~s~"\n)"; 91 } 92 93 94 T toStruct(T)(string struc) pure nothrow 95 { 96 T ret; 97 string[] each; 98 string currentArg; 99 100 bool isInsideString = false; 101 for(size_t i = 1; i < (cast(int)struc.length)-1; i++) 102 { 103 if(!isInsideString && struc[i] == ',') 104 { 105 each~= currentArg; 106 currentArg = null; 107 if(struc[i+1] == ' ') 108 i++; 109 continue; 110 } 111 else if(struc[i] == '"') 112 { 113 isInsideString = !isInsideString; 114 continue; 115 } 116 currentArg~= struc[i]; 117 } 118 if(currentArg.length != 0) 119 each~=currentArg; 120 121 static foreach(i, m; __traits(allMembers, T)) 122 {{ 123 alias member = __traits(getMember, ret, m); 124 member = to!(typeof(member))(each[i]); 125 }} 126 return ret; 127 } 128 129 130 bool toBool(string str) pure nothrow @safe @nogc {return str == "true";} 131 132 ///Use that for making toStruct easier 133 string toString(string str) pure nothrow @safe @nogc {return str;} 134 135 136 string toString(bool b) pure nothrow @safe @nogc 137 { 138 return b ? "true" : "false"; 139 } 140 141 TO to(TO, FROM)(FROM f) pure nothrow 142 { 143 static if(is(TO == string)) 144 { 145 static if(is(FROM == const(char)*) || is(FROM == char*)) 146 return fromStringz(f); 147 else static if(is(FROM == enum)) 148 return toString!FROM(f); 149 else 150 return toString(f); 151 } 152 else static if(is(TO == int)) 153 { 154 static if(!is(FROM == string)) 155 return toInt(f.toString); 156 else 157 return toInt(f); 158 } 159 else static if(is(TO == uint) || is(TO == size_t) || is(TO == ubyte) || is(TO == ushort)) 160 { 161 static if(!is(FROM == string)) 162 return cast(TO)toInt(f.toString); 163 else 164 return cast(TO)toInt(f); 165 } 166 else static if(is(TO == float)) 167 { 168 static if(!is(FROM == string)) 169 return toFloat(f.toString); 170 else 171 return toFloat(f); 172 } 173 else static if(is(TO == bool)) 174 { 175 static if(!is(FROM == string)) 176 return toBool(f.toString); 177 else 178 return toBool(f); 179 } 180 else 181 { 182 static if(!is(FROM == string)) 183 return toStruct!TO(f.toString); 184 else 185 return toStruct!TO(f); 186 } 187 } 188 189 /// This function can be called at compilation time without bringing runtime 190 string toString(long x) pure nothrow @safe 191 { 192 enum numbers = "0123456789"; 193 bool isNegative = x < 0; 194 if(isNegative) 195 x*= -1; 196 size_t div = 10; 197 int length = 1; 198 int count = 1; 199 while(div <= x) 200 { 201 div*=10; 202 length++; 203 } 204 if(isNegative) length++; 205 char[] ret = new char[](length); 206 if(isNegative) 207 ret[0] = '-'; 208 div = 10; 209 while(div <= x) 210 { 211 count++; 212 ret[length-count]=numbers[cast(size_t)((x/div)%10)]; 213 div*=10; 214 } 215 ret[length-1] = numbers[cast(size_t)(x%10)]; 216 return ret[0..$]; 217 } 218 219 220 string toString(float f) pure nothrow @safe 221 { 222 if(f != f) return "nan"; 223 else if(f == -float.infinity) return "-inf"; 224 else if(f == float.infinity) return "inf"; 225 226 bool isNegative = f < 0; 227 if(isNegative) 228 f = -f; 229 230 float decimal = f - cast(int)f; 231 string ret = (cast(int)f).toString; 232 if(isNegative) 233 ret = "-"~ret; 234 235 if(decimal == 0) 236 return ret; 237 ret~= '.'; 238 long multiplier = 10; 239 while(cast(long)(decimal*multiplier) < (decimal*multiplier)) 240 { 241 if(cast(long)(decimal*multiplier) == 0) 242 ret~= '0'; 243 multiplier*=10; 244 } 245 return ret ~ (cast(long)(decimal*multiplier)).toString; 246 } 247 248 pure string toString(void* ptr) @safe nothrow 249 { 250 return ptr is null ? "null" : toHex(cast(size_t)ptr); 251 } 252 253 254 pure string toHex(size_t n) @safe nothrow 255 { 256 enum numbers = "0123456789ABCDEF"; 257 int preAllocSize = 1; 258 ulong div = 16; 259 while(div <= n) 260 { 261 div*= 16; 262 preAllocSize++; 263 } 264 div/= 16; 265 char[] ret = new char[](preAllocSize); 266 int i = 0; 267 268 while(div >= 16) 269 { 270 ret[i++] = numbers[(n/div)%16]; 271 div/= 16; 272 } 273 ret[i] = numbers[n%16]; 274 return ret[0..$]; 275 } 276 277 278 string fromUTF16(wstring str) pure nothrow 279 { 280 string ret; 281 foreach(c;str) ret~= c; 282 return ret; 283 } 284 285 int toInt(string str) pure nothrow @safe @nogc 286 { 287 if(str.length == 0) return 0; 288 str = str.trim; 289 290 int i = (cast(int)str.length)-1; 291 292 int last = 0; 293 int multiplier = 1; 294 int ret = 0; 295 if(str[0] == '-') 296 { 297 last++; 298 multiplier*= -1; 299 } 300 for(; i >= last; i--) 301 { 302 if(str[i] >= '0' && str[i] <= '9') 303 ret+= (str[i] - '0') * multiplier; 304 else 305 return ret; 306 multiplier*= 10; 307 } 308 return ret; 309 } 310 311 312 float toFloat(string str) pure nothrow @safe @nogc 313 { 314 if(str.length == 0) return 0; 315 str = str.trim; 316 if(str == "nan" || str == "NaN") return float.init; 317 if(str == "inf" || str == "infinity" || str == "Infinity") return float.infinity; 318 319 int integerPart = 0; 320 int decimalPart = 0; 321 int i = 0; 322 bool isNegative = str[0] == '-'; 323 if(isNegative) 324 str = str[1..$]; 325 bool isDecimal = false; 326 for(; i < str.length; i++) 327 { 328 if(str[i] =='.') 329 { 330 isDecimal = true; 331 continue; 332 } 333 if(isDecimal) 334 decimalPart++; 335 else 336 integerPart++; 337 } 338 339 if(decimalPart == 0) 340 return (isNegative ? -1 : 1) * cast(float)str.toInt; 341 342 i = 0; 343 float decimal= 0; 344 float integer = 0; 345 int integerMultiplier = 1; 346 float floatMultiplier = 1.0f/10.0f; 347 348 while(integerPart > 0) 349 { 350 //Iterate the number from backwards towards the greatest value 351 integer+= (str[integerPart - 1] - '0') * integerMultiplier; 352 integerMultiplier*= 10; 353 integerPart--; 354 i++; 355 } 356 i++; //Jump the . 357 while(decimalPart > 0) 358 { 359 decimal+= (str[i] - '0') * floatMultiplier; 360 floatMultiplier/= 10; 361 decimalPart--; 362 i++; 363 } 364 return (integer + decimal) * (isNegative ? -1 : 1); 365 } 366 367 368 unittest 369 { 370 assert(toString(500) == "500"); 371 assert(toFloat("50.5") == 50.5); 372 assert(toString(100.0) == "100"); 373 assert(toInt("-500") == -500); 374 assert(toString("Hello") == "Hello"); 375 assert(toString(true) == "true"); 376 assert(toString(50.25)== "50.25"); 377 assert(toString(0.003) == "0.003"); 378 assert(toString(0.999) == "0.999"); 379 }